designer <- function(x,  N, drop, Y, corr, Y_binary, u_m, u_sd, multiblock, block_many_vars) {

  ## Model
  model_helper_function <- function(N) {
    sim_data <- x %>%
      # sample first
      sample_n(size = N, replace = TRUE) %>%
      # then do simulated variables
      rename(Y = {{Y}}) %>%
      mutate(
        var_1 = corr * (Y - mean(Y)) / sd(Y) + sqrt(1 - corr^2) * rnorm(n()), 
        var_2 = corr * (Y - mean(Y)) / sd(Y) + sqrt(1 - corr^2) * rnorm(n()),
        block_one_var = cut(var_1, breaks = 5, labels = 1:5),
        Y0 = var_2,
       )  %>%
      # remove unneeded ones
      select(-var_1, -var_2) %>%
      # create an identifier
      mutate(ID = 1:N)
    
    if(Y_binary){
      sim_data <- sim_data %>%
        mutate(
          Y0 = ifelse(Y0 < median(Y0), 0, 1)
        )
    }
    
    if(multiblock == "continuous"){
      sim_data <- sim_data %>%
        mutate(block_one_var = as.numeric(as.character(block_one_var)))
    }
    if(multiblock == "discrete"){
      sim_data <- sim_data %>%
        mutate(block_one_var = as.character(block_one_var))
    }
    
    # create "true" model, then remove Y and Z
    mod = lm(Y ~ ., data = sim_data %>% select(-ID))
    sim_data <- sim_data %>%
      select(-Y, -Z)

    sim_data <- sim_data %>%
      mutate(
        u = rnorm(N, u_m, u_sd),
        Y_Z_1_pred = u + predict(mod,
                                 newdata = sim_data %>% mutate(Z=1) %>% select(-ID),
                                 type = "response"),
        Y_Z_0_pred = u + predict(mod,
                                 newdata = sim_data %>% mutate(Z=0) %>% select(-ID),
                                 type = "response"))
    
    if(Y_binary){
      sim_data <- sim_data %>%
        mutate(Y_Z_1 = if_else(Y_Z_1_pred > 0.5, 1, 0),
               Y_Z_0 = if_else(Y_Z_0_pred > 0.5, 1, 0))
      
    }
    
    if(!Y_binary){
      sim_data <- sim_data %>% 
        mutate(Y_Z_1 = Y_Z_1_pred,
               Y_Z_0 = Y_Z_0_pred)
    }
    
    data.frame(sim_data)
  }
  
  model = declare_model(N = N, handler = model_helper_function)
  
  ## Inquiry
  estimand = declare_inquiry(ATE = mean(Y_Z_1 - Y_Z_0))
  
  ## Data strategy
  # Indicator to see if unit drops or not under alt designs
  report = declare_potential_outcomes(R = complete_rs(N, prob = 1-drop))
  retained = declare_sampling(S = R == 1)
  
  # Complete randomization
  complete_rand = declare_assignment(Z_com = complete_ra(N = N))
  
  # Block randomization -- hard coded to be 5 levels
  block_one = declare_assignment(Z_one = block_ra(blocks = block_one_var))
  
  # Block all
  # Create multivariate blocks
  if(multiblock == "continuous"){
    create_multivar_blocks = declare_step(handler = function(data){
      suppressWarnings({
        out = blockTools::block(data,
                                n.tr = 2,
                                algorithm = "optimal",
                                id.vars = "ID",
                                block.vars = c(block_many_vars, "block_one_var"))
        
        # Note: Warning when there is an odd number of units
        
        # Note: this will create a block ID for a 1-person block
        # when there are an odd number of units
        data$block_id_multivar = createBlockIDs(out, data, id.var = "ID")
      })
      
      return(data)
    })
  }
  
  if(multiblock == "discrete"){
    create_multivar_blocks = declare_step(handler = function(data){
      
      # Make one blocking variable out of intersecting blocks
      b = data %>% 
        select(unlist(block_many_vars), "block_one_var") %>% 
        unite(col = b, sep = "-") %>% 
        .$b
      
      data$block_id_multivar = b
      
      return(data)
    })
  }
  
  block_all = declare_assignment(Z_all = block_ra(blocks = block_id_multivar))
  
  # reveal outcomes
  measurement = declare_measurement(Y_com = case_when(Z_com == 1 ~ Y_Z_1,
                                                      Z_com == 0 ~ Y_Z_0,
                                                      .default = NA),
                                    Y_one = case_when(Z_one == 1 ~ Y_Z_1,
                                                      Z_one == 0 ~ Y_Z_0,
                                                      .default = NA),
                                    Y_all = case_when(Z_all == 1 ~ Y_Z_1,
                                                      Z_all == 0 ~ Y_Z_0,
                                                      .default = NA))

  
  
  ## Answer strategy
  # 1. Complete randomization + post-only measurement of the outcome (Standard)
  est1 = declare_estimator(Y_com ~ Z_com,
                           .method = lm_robust,
                           se_type = "stata",
                           inquiry = estimand,
                           label = "Complete + Post-only")
  
  # 2. Complete randomization + (quasi) pre-post measurement of the outcome
  est2 = declare_estimator(Y_com ~ Z_com + Y0,
                           .method = lm_robust,
                           se_type = "stata",
                           inquiry = estimand,
                           label = "Complete + Pre-post")
  
  est3 = declare_estimator(Y_one ~ Z_one,
                           .method = lm_robust,
                           fixed_effects = block_one_var,
                           se_type = "stata",
                           inquiry = estimand,
                           label = "Block one + Post-only")
  
  # 4. Block randomization on one covariate + pre-post
  est4 = declare_estimator(Y_one ~ Z_one + Y0,
                           .method = lm_robust,
                           fixed_effects = block_one_var,
                           se_type = "stata",
                           inquiry = estimand,
                           label = "Block one + Pre-post")
  
  # 5. Block randomization on all of the selected blocking covariates + post-only
  est5 = declare_estimator(Y_all ~ Z_all,
                           .method = lm_robust,
                           fixed_effects = ~block_id_multivar,
                           se_type = "stata",
                           inquiry = estimand,
                           label = "Block all + Post-only")
  
  # 6. Block randomization on all of the the selected covariates + pre-post
  est6 = declare_estimator(Y_all ~ Z_all + Y0,
                           .method = lm_robust,
                           fixed_effects = ~block_id_multivar,
                           se_type = "stata",
                           inquiry = estimand,
                           label = "Block all + Pre-post")
  
    design = model +
      estimand + 
      report + retained +
      complete_rand +
      block_one +  
      create_multivar_blocks + block_all +
      measurement +
      est1 + est2 + est3 +
      est4 + est5 + est6 
    
    return(design)
}
